package cn.org.xiaosheng.server.manager;

import cn.org.xiaosheng.core.factory.ServiceFactory;
import cn.org.xiaosheng.core.handler.HeartBeatServerHandler;
import cn.org.xiaosheng.core.handler.PingMessageHandler;
import cn.org.xiaosheng.core.protocol.MessageCodecSharable;
import cn.org.xiaosheng.core.protocol.ProcotolFrameDecoder;
import cn.org.xiaosheng.core.registery.NacosServerRegistry;
import cn.org.xiaosheng.core.registery.ServerRegistry;
import cn.org.xiaosheng.core.utils.PackageScanUtils;
import cn.org.xiaosheng.server.annotation.EnableRpcServer;
import cn.org.xiaosheng.server.annotation.RpcServer;
import cn.org.xiaosheng.server.annotation.RpcServerScan;
import cn.org.xiaosheng.server.handler.RpcRequestMessageHandler;
import com.alibaba.nacos.api.exception.NacosException;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 *
 * Rpc服务端管理器
 * @author XiaoSheng
 * @date 2024/8/22 上午11:52
 */
@Slf4j
public class RpcServiceManager extends ChannelOutboundHandlerAdapter {

    protected String host;
    protected int port;
    protected ServerRegistry serverRegistry;
    protected ServiceFactory serviceFactory;
    NioEventLoopGroup worker = new NioEventLoopGroup();
    NioEventLoopGroup boss = new NioEventLoopGroup();
    ServerBootstrap bootstrap = new ServerBootstrap();

    public RpcServiceManager(String host, int port) {
        this.host = host;
        this.port = port;
        serverRegistry = new NacosServerRegistry();
        serviceFactory = new ServiceFactory();
        autoRegistry();
    }

    /**
     * 开启服务
     */
    public void start() {
        //日志
        LoggingHandler LOGGING = new LoggingHandler(LogLevel.DEBUG);
        //消息解码器
        MessageCodecSharable MESSAGE_CODEC = new MessageCodecSharable();
        //RPC请求处理器
        RpcRequestMessageHandler RPC_HANDLER = new RpcRequestMessageHandler();
        //心跳处理器
        HeartBeatServerHandler HEATBEAT_SERVER = new HeartBeatServerHandler();
        //心跳请求的处理器
        PingMessageHandler PINGMESSAGE = new PingMessageHandler();
        try {
            bootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 256)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
                            pipeline.addLast(new ProcotolFrameDecoder());//定长解码器
                            pipeline.addLast(MESSAGE_CODEC);
                            pipeline.addLast(LOGGING);
                            pipeline.addLast(HEATBEAT_SERVER);
                            pipeline.addLast(PINGMESSAGE);
                            pipeline.addLast(RPC_HANDLER);
                        }
                    });
            //绑定端口
            Channel channel = bootstrap.bind(port).sync().channel();
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.err.println("启动服务出错");
        }finally {
            worker.shutdownGracefully();
            boss.shutdownGracefully();
        }

    }

    /**
     * 自动扫描@RpcServer注解  注册服务
     */
    public void autoRegistry() {
        // 获取启动类路径
        String mainClassPath = PackageScanUtils.getStackTrace();
        Class<?> mainClass;
        log.info("mainClassPath:" + mainClassPath);
        try {
            mainClass = Class.forName(mainClassPath);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("启动类未找到!");
        }
        log.info("mainClass:" + mainClass);

        if (!mainClass.isAnnotationPresent(EnableRpcServer.class)) {
            throw new RuntimeException("启动类缺少@EnableRpcServer注解!");
        } else {
            if (!mainClass.getAnnotation(EnableRpcServer.class).enable()) {
                throw new RuntimeException("请开启@EnableRpcServer注解");
            }
        }

        String annotationPath = mainClass.getAnnotation(RpcServerScan.class).value();
        // 如果注解的路径为空，则默认从main父路径包下进行查找
        if(null == annotationPath || "".equals(annotationPath)) {
            annotationPath = mainClassPath.substring(0, mainClassPath.lastIndexOf("."));
        }

        // 获取所有类的set集合
        Set<Class<?>> classSet = PackageScanUtils.getClasses(annotationPath);
        for (Class<?> c : classSet) {
            // 只注册含有@RpcServer注解的类
            if (c.isAnnotationPresent(RpcServer.class)) {
                String serverNameValue = c.getAnnotation(RpcServer.class).name();
                Object object;

                try {
                    object = c.newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                    log.error("创建对象" + c + "发生错误");
                    continue;
                }
                // 若注解值为空，则使用类名
                if ("".equals(serverNameValue)) {
                    // 将简单类注入到服务中
                    addServer(c.getSimpleName(), object);
                } else {
                    addServer(serverNameValue, object);
                }
            }
        }
    }

    /**
     * 添加对象到工厂和注册到注册中心
     *
     * @param serverName
     * @param server
     * @param <T>
     * @throws NacosException
     */
    public <T> void addServer(String serverName, T server) {
        serviceFactory.addServiceProvider(serverName, server);
        serverRegistry.register(serverName, new InetSocketAddress(host, port));
    }

}
